// ========== Copyright Header Begin ==========================================
// 
// OpenSPARC T1 Processor File: Ni_Cpu.cc
// Copyright (c) 2006 Sun Microsystems, Inc.  All Rights Reserved.
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES.
// 
// The above named program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License version 2 as published by the Free Software Foundation.
// 
// The above named program is distributed in the hope that it will be 
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
// 
// You should have received a copy of the GNU General Public
// License along with this work; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
// 
// ========== Copyright Header End ============================================
/************************************************************************
**  
**  Copyright (C) 2002, Sun Microsystems, Inc.
**
**  Sun considers its source code as an unpublished, proprietary
**  trade secret and it is available only under strict license provisions.
**  This copyright notice is placed here only to protect Sun in the event 
**  the source is deemed a published work. Disassembly, decompilation,
**  or other means of reducing the object code to human readable form
**  is prohibited by the license agreement under which this code is
**  provided to the user or company in possession of this copy."
**
*************************************************************************/
#include "Ni/Ni_Cpu.h"
#include "Ni/Ni_Strand.h"
#include "Ni/Ni_InterruptUnit.h"
#include "Ni/Ni_Trap.h"
#include "Blaze_CBInterface.h"
#include "StdMacros.h"
#include <sstream>
#include <assert.h>

using namespace std;
using namespace Riesling;


Ni_Cpu::Ni_Cpu(Ni_SystemBase& _system, uint_t cpu_id, uint_t max_cpu_id)
 :
  system_(_system),
  core0_(*this,(8*cpu_id+0),(8*max_cpu_id)), 
  core1_(*this,(8*cpu_id+1),(8*max_cpu_id)),
  core2_(*this,(8*cpu_id+2),(8*max_cpu_id)), 
  core3_(*this,(8*cpu_id+3),(8*max_cpu_id)),
  core4_(*this,(8*cpu_id+4),(8*max_cpu_id)), 
  core5_(*this,(8*cpu_id+5),(8*max_cpu_id)),
  core6_(*this,(8*cpu_id+6),(8*max_cpu_id)), 
  core7_(*this,(8*cpu_id+7),(8*max_cpu_id))
{
  for (int j = 0; j < getNCores(); j++) 
  {
    Ni_Core* core = dynamic_cast<Ni_Core *>(getCorePtr(j));
    for (int k = 0; k < core->getNStrands(); k++) 
    {
      Ni_Strand* strand = dynamic_cast<Ni_Strand *>(core->getStrandPtr(k));
      strand->getAsiDispatcherPtr()->registerHandler( &cmpRegfile_ );
      strand->getAsiDispatcherPtr()->registerHandler( this );
    }
  }
}

Ni_Cpu::~Ni_Cpu()
{}

string
Ni_Cpu::toString() const
{
    ostringstream os;

    os << core0_.toString() << "\n";
    os << core1_.toString() << "\n";
    os << core2_.toString() << "\n";
    os << core3_.toString() << "\n";
    os << core4_.toString() << "\n";
    os << core5_.toString() << "\n";
    os << core6_.toString() << "\n";
    os << core7_.toString() << "\n";
    os << cmpRegfile_.toString();

    return os.str();
}

void
Ni_Cpu::step()
{
    core0_.step();
    core1_.step();
    core2_.step();
    core3_.step();
    core4_.step();
    core5_.step();
    core6_.step();
    core7_.step();
}

void
Ni_Cpu::setAddrSpace( AddressSpace *addressSpace )
{
    core0_.setAddrSpace( addressSpace );
    core1_.setAddrSpace( addressSpace );
    core2_.setAddrSpace( addressSpace );
    core3_.setAddrSpace( addressSpace );
    core4_.setAddrSpace( addressSpace );
    core5_.setAddrSpace( addressSpace );
    core6_.setAddrSpace( addressSpace );
    core7_.setAddrSpace( addressSpace );
}

uint_t 
Ni_Cpu::getNCores() const
{
    return 8;
}

Core *
Ni_Cpu::getCorePtr( uint_t coreNr )
{
    switch (coreNr)
    {
      case 0: return &core0_;
      case 1: return &core1_;
      case 2: return &core2_;
      case 3: return &core3_;
      case 4: return &core4_;
      case 5: return &core5_;
      case 6: return &core6_;
      case 7: return &core7_;
      default: assert(0);
    }
}

void
Ni_Cpu::setParentRegistry( SimRegistry *regParent )
{
    reg_.setParent( regParent );
    
    reg_.addInstance( "core0", SimRegistry::CORE, &core0_ );
    reg_.addInstance( "core1", SimRegistry::CORE, &core1_ );
    reg_.addInstance( "core2", SimRegistry::CORE, &core2_ );
    reg_.addInstance( "core3", SimRegistry::CORE, &core3_ );
    reg_.addInstance( "core4", SimRegistry::CORE, &core4_ );
    reg_.addInstance( "core5", SimRegistry::CORE, &core5_ );
    reg_.addInstance( "core6", SimRegistry::CORE, &core6_ );
    reg_.addInstance( "core7", SimRegistry::CORE, &core7_ );
    
    core0_.setParentRegistry( &reg_ ); 
    core1_.setParentRegistry( &reg_ ); 
    core2_.setParentRegistry( &reg_ ); 
    core3_.setParentRegistry( &reg_ ); 
    core4_.setParentRegistry( &reg_ ); 
    core5_.setParentRegistry( &reg_ ); 
    core6_.setParentRegistry( &reg_ ); 
    core7_.setParentRegistry( &reg_ ); 
}

void 
Ni_Cpu::asiWriteHandler( AsiDispatchPacket& p )
{

    p.setResponse( AsiDispatchPacket::OK );

    switch( p.getAccessIndex() ){
	case Ni_InterruptUnit::SWVR_UDB_INTR_W:
	    {
	        uint64_t data = p.getData();
		int type = (data >> 16) & 0x3;
		int vc_id = (data >> 8) & 0x1f;
		int v = data & 0x3f;
		if (type == 0) { // 0:interrupt
		  // If not in cosim mode, send interrupt here, else wait until RTL
		  // testbench signal it. 
		  if (!BlazeCBInterface::cosim()) {
		    sendInterrupt(vc_id, v);
		  }
		  else
		    cerr << "ToDo: Write to UDB_INTR_W by software. ignored - waiting RTL cosim to sendInterrupt" << endl;
		}
	    }
	    break;

	default:
	    p.setResponse( AsiDispatchPacket::HANDLER_ERROR );
	    break;

    }

}

void 
Ni_Cpu::asiReadHandler( AsiDispatchPacket& p )
{

    p.setResponse( AsiDispatchPacket::OK );

    switch( p.getAccessIndex() ){
	case Ni_InterruptUnit::SWVR_UDB_INTR_W:
	    RIESLING_TRAP(Ni_Trap::data_access_exception);
	    break;

	default:
	    p.setResponse( AsiDispatchPacket::HANDLER_ERROR );
	    break;

    }

}

void 
Ni_Cpu::setAsiDispatcher( AsiDispatcher &dispatcher )
{
}


void 
Ni_Cpu::sendInterrupt( int thread, int vector )
{
    Ni_Strand 	*sPtr;
    int		c, s;

    s = thread & 0x3;			// destination thread
    c = (thread >> 2) & 0x7;		// destination core

    sPtr =  dynamic_cast<Ni_Strand *>(getCorePtr(c)->getStrandPtr(s));

    if( !sPtr ) {
        RIESLING_THROW_DOMAIN_ERROR("can't cast sPtr to Ni_Strand*");
    }

    sPtr->sendResumeMessage();	// make it active so it can take my call

    sPtr->getInterruptUnitPtr()->setIntrReceiveVector(vector);
}


void 
Ni_Cpu::setThreadStatusSpeculativeEnable( bool v )
{
  core0_.setThreadStatusSpeculativeEnable(v);
  core1_.setThreadStatusSpeculativeEnable(v);
  core2_.setThreadStatusSpeculativeEnable(v);
  core3_.setThreadStatusSpeculativeEnable(v);
  core4_.setThreadStatusSpeculativeEnable(v);
  core5_.setThreadStatusSpeculativeEnable(v);
  core6_.setThreadStatusSpeculativeEnable(v);
  core7_.setThreadStatusSpeculativeEnable(v);
}

void 
Ni_Cpu::snapshot( SS_SnapShot &ss, const char *prefix )
{
    char extended_prefix[SS_SnapShot::LINE_SIZE];
    sprintf(extended_prefix,"%s.core0",prefix); core0_.snapshot(ss, extended_prefix);
    sprintf(extended_prefix,"%s.core1",prefix); core1_.snapshot(ss, extended_prefix);
    sprintf(extended_prefix,"%s.core2",prefix); core2_.snapshot(ss, extended_prefix);
    sprintf(extended_prefix,"%s.core3",prefix); core3_.snapshot(ss, extended_prefix);
    sprintf(extended_prefix,"%s.core4",prefix); core4_.snapshot(ss, extended_prefix);
    sprintf(extended_prefix,"%s.core5",prefix); core5_.snapshot(ss, extended_prefix);
    sprintf(extended_prefix,"%s.core6",prefix); core6_.snapshot(ss, extended_prefix);
    sprintf(extended_prefix,"%s.core7",prefix); core7_.snapshot(ss, extended_prefix);
    sprintf(extended_prefix,"%s.cmpregs",prefix); cmpRegfile_.snapshot(ss, extended_prefix);
}
